import Axios from "axios";

const BASE_CONFIG = {
	baseURL: "",
	timeout: 1000 * 60 * 2,
	method: "POST",
};
const DEFAULT_DENY_CHARS = ["", null, -1];

const EMPTY_OBJECT = {};

const EMPTY_FUNC = function() {};

let aixos;

export default class Api {
	constructor({
		apiUrls = EMPTY_OBJECT,
		staticUrls = EMPTY_OBJECT, //静态访问地址
		baseConfig = EMPTY_OBJECT,
		codeCatcher, // 状态码
		bodyAllowChars = [],
		paramsAllowChars = [],
	} = {}) {
		if (codeCatcher === undefined) throw Error("codeCatcher 是必须的");
		this._apiUrls = apiUrls;
		this._staticUrls = staticUrls;
		this._baseConfig = {
			...baseConfig,
			...BASE_CONFIG,
		};
		// 创建axios实例
		this.aixos = aixos = Axios.create({
			...baseConfig,
		});
		// 添加响应拦截器
		aixos.interceptors.response.use(
			(response) => this._transformResponse(response),
			(error) => {
				this._trigger("REQUEST", error);
				return Promise.resolve({
					success: false,
					response: error,
				});
			}
		);
		// 添加请求拦截器
		aixos.interceptors.request.use((config) => this._onRequest(config));
		this._buildStaticUrls(staticUrls);
		this._buildApiUrls(apiUrls, this);
		this.$codeCatcher = codeCatcher;
		this.$map = new Map(); //定义一个空的map
		this.$resCatchMap = new Map();
		this.$bodyAllowChars = bodyAllowChars;
		this.$paramsAllowChars = paramsAllowChars;
	}
	// 请求失败时
	$catch(value, cb) {
		if (Array.isArray(value)) {
			value.forEach((val) => {
				this.$resCatchMap.set(val, cb);
			});
		} else {
			this.$resCatchMap.set(value, cb);
		}
	}
	// 监听请求
	$on(value, cb, scope) {
		if (this.$map.get(value) === undefined) {
			this.$map.set(value, []);
		}
		this.$map.get(value).push({
			fn: cb,
			scope,
		});
	}
	// 注销监听请求
	$off(value, cb) {
		let cbList = this.$map[value];
		if (cbList) {
			let index = cbList.findIndex((fun) => fun === cb);
			cbList.splice(index, 1);
		}
	}
	// 监听一次请求
	$once(value, cb, scope) {
		let _cb = (data, arg) => {
			cb(data, arg);
			this.off(value, _cb);
		};
		this.on(value, _cb, scope);
	}
	// 触发请求时,this.$map ==> {0=>{REQUEST:error},1=>{RESPONSE:response},3=>{ERROR:err}}
	_trigger(value, params) {
		let cbList = this.$map.get(value);
		if (cbList !== undefined) {
			cbList.forEach(({
				fn,
				scope
			}) => {
				if (scope) {
					fn.call(scope, params);
				} else {
					fn(params);
				}
			});
		}
	}
	// 处理响应时返回的数据
	_onRequest(config) {
		let newConfig = {};
		let cbList = this.$map.get("REQUEST");
		if (cbList !== undefined) {
			cbList.forEach(({
				fn,
				scope
			}) => {
				let _config;
				if (scope) {
					_config = fn.call(scope, config);
				} else {
					_config = fn(config);
				}
				newConfig = {
					...newConfig,
					..._config,
				};
			});
		}
		// console.log(newConfig);
		return {
			...config,
			...newConfig,
		};
	}
	// 在传递给 then/catch 前，允许修改响应数据
	_transformResponse(response) {
		let {
			data
		} = response;
		let resData;
		let value = this.$codeCatcher(data, response);
		let cb = this.$resCatchMap.get(value);
		if (cb !== undefined) {
			resData = cb(response);
			// console.log(resData)
		}
		this._trigger("RESPONSE", response);
		return Promise.resolve({
			success: false,
			response,
			data: response.data,
			...resData,
		});
	}
	//处理静态接口地址，因用了路由插件，$表示get请求,所以得处理url地址;若无使用路由插件可以忽略。
	_buildStaticUrls(staticUrls) {
		let urls = staticUrls;
		Object.keys(urls).forEach((key) => {
			let url = urls[key];
			if (url.indexOf("$") === 0) {
				this[key] = url.slice(1);
			} else if (url.indexOf("http") !== 0) {
				this[key] = this._baseConfig.baseURL + url;
			}
		});
	}
	// 处理Ajax请求地址
	_buildApiUrls(apiUrls = {}, list = {}) {
		Object.keys(apiUrls).forEach((key) => {
			let val = apiUrls[key];
			switch (val.constructor) {
				case String:
					list[key] = this._createAxios({
						...this._parseUrl(val),
					});
					break;
				case Object:
					if (val.url !== undefined) {
						list[key] = this._createAxios({
							...val,
						});
					} else {
						list[key] = this._buildApiUrls(val);
					}
			}
		});
		return list;
	}
	// 因用了路由插件，$表示get请求,否则默认为post
	_parseUrl(url) {
		return url.indexOf("$") === 0 ? {
			url: url.slice(1),
			method: "GET",
		} : {
			url,
			method: "POST",
		};
	}
	// 创建请求
	_createAxios(api) {
		return (options = {}) => {
			let config = {
				...api,
				...options,
			};
			let {
				data, // post ，put，putch等传值 -> data:{test:""}
				params, // get传值 -> params:{test:""}
				query, // 路径传参
				bodyAllowChars = [], // 请求data字符过滤白名单
				paramsAllowChars = [], // 请求params字符过滤白名单
			} = config;
			bodyAllowChars = new Set([...this.$bodyAllowChars, ...bodyAllowChars]);
			paramsAllowChars = new Set([...this.$paramsAllowChars, ...paramsAllowChars]);
			if (data !== undefined && !(data instanceof FormData)) {
				config.data = removeEmptyProps(data, Array.from(bodyAllowChars));
			}
			if (params != undefined) {
				config.params = removeEmptyProps(params, Array.from(paramsAllowChars));
			}
			if (query && /\{\w+?\}/g.test(config.url)) { // 判断是否是url格式
				Object.keys(options.query).forEach((key) => {
					let val = options.query[key];
					config.url = config.url.replace(new RegExp(`\{${key}\}`), val);
				});
			}

			return aixos.request(config).catch((err) => {
				this._trigger("ERROR", err);
			});
		};
	}
}
// 处理空格
function removeEmptyProps(ob, allowChars = []) {
	let result = {};
	let map = allowChars.length ?
		DEFAULT_DENY_CHARS.concat(allowChars).filter(function(v) {
			return (
				DEFAULT_DENY_CHARS.indexOf(v) === -1 || allowChars.indexOf(v) === -1
			);
		}) :
		DEFAULT_DENY_CHARS;

	for (let key in ob) {
		let val = ob[key];
		let type = typeof val;
		if (!map.includes(type === "string" ? val.trim() : val)) {
			result[key] = ob[key];
		}
	}
	return result;
}
